#include "qstorage.h"


Storage::Storage(const QString &fullPath, QObject *parent) : QObject(parent), m_path(fullPath)
{
    QMutexLocker l(&m_mutex);

    /* 若文件不存在则创建空文件 */
    if (!QFile::exists(m_path))
    {
        QFile f(m_path);
        f.open(QIODevice::WriteOnly);
        f.close();
    }

    /* 启动只读最后 100 条到内存 */
    QFile file(m_path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        emit error(file.errorString());
        return;
    }
    qint64 tail = file.size();
    m_filePos = tail;
    QTextStream ts(&file);
    int left = 100;
    QStringList lines;
    while (left > 0 && ts.pos() > 0)
    {
        qint64 readLen = qMin(8192LL, ts.pos());
        QByteArray block = file.read(readLen);
        ts.seek(ts.pos() - readLen);
        QString chunk = QString::fromUtf8(block);
        int lastNL = chunk.lastIndexOf('\n');
        if (lastNL <= 0)
        {                                // ① 无完整行
            ts.seek(ts.pos() - readLen); // 整块作废
            continue;
        }

        QStringList tmp = chunk.left(lastNL).split('\n', Qt::SkipEmptyParts);
        if (!tmp.isEmpty())
            tmp.removeFirst(); // ② 丢弃残缺首行
        if (tmp.size() > left)
            tmp = tmp.mid(tmp.size() - left);
        lines = tmp + lines;
        left -= tmp.size();

        qint64 consumed = block.size() - lastNL;
        ts.seek(ts.pos() - consumed); // ③ 真正消耗
    }
    m_filePos = ts.pos();
    file.close();

    for (const QString &line : lines)
    {
        QJsonDocument doc = QJsonDocument::fromJson(line.toUtf8());
        if (doc.isObject())
            m_cache.append(doc.object());
    }
    m_flushedCount = m_cache.size(); // 已落盘条数 = 初始条数

    /* 后台定时刷盘：1 秒 */
    connect(&m_flushTimer, &QTimer::timeout, this, &Storage::onFlush);
    m_flushTimer.start(1000);
}

QVector<QJsonObject> Storage::hotRecords() const
{
    QMutexLocker l(&m_mutex);
    return m_cache;
}

int Storage::loadMore()
{
    QMutexLocker l(&m_mutex);
    QFile file(m_path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return 0;

    QTextStream ts(&file);
    qint64 startPos = qMax(0LL, m_filePos - 8192);
    ts.seek(startPos);
    QString chunk = ts.read(m_filePos - startPos);
    m_filePos = startPos;

    QStringList lines = chunk.split('\n', Qt::SkipEmptyParts);
    if (!chunk.endsWith('\n'))
        lines.removeLast();

    int added = 0;
    for (int i = lines.size() - 1; i >= 0; --i)
    {
        QJsonDocument doc = QJsonDocument::fromJson(lines.at(i).toUtf8());
        if (doc.isObject())
        {
            m_cache.prepend(doc.object());
            ++added;
        }
    }
    return added;
}

void Storage::append(const QJsonObject &rec)
{
    QMutexLocker l(&m_mutex);
    m_cache.append(rec);
    m_dirty = true;
}
bool Storage::saveAll()
{
    QMutexLocker l(&m_mutex);
    QSaveFile sf(m_path);
    if (!sf.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        emit error(sf.errorString());
        return false;
    }
    QTextStream ts(&sf);
    for (const auto &rec : m_cache)
        ts << QJsonDocument(rec).toJson(QJsonDocument::Compact) << "\n";
    bool ok = sf.commit();
    if (ok)
    {
        m_flushedCount = m_cache.size();
        m_dirty = false;
    }
    return ok;
}

void Storage::clear()
{
    QMutexLocker l(&m_mutex);
    m_cache.clear();
    m_filePos = 0;
    m_flushedCount = 0;
    m_dirty = true;
    QFile(m_path).resize(0);
}

void Storage::onFlush()
{
    if (!m_dirty)
        return;
    QSaveFile sf(m_path);
    if (!sf.open(QIODevice::WriteOnly | QIODevice::Append))
    {
        emit error(sf.errorString());
        return;
    }
    QTextStream ts(&sf);
    for (int i = m_flushedCount; i < m_cache.size(); ++i)
        ts << QJsonDocument(m_cache.at(i)).toJson(QJsonDocument::Compact) << "\n";
    sf.commit(); // 原子提交
    m_flushedCount = m_cache.size();
    m_dirty = false;
}
